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"
|