tlslite/utils/python_tripledes.py
#################################################
# Documentation #
#################################################
# Author: Todd Whiteman
# Date: 16th March, 2009
# Verion: 2.0.0
# License: Public Domain - free to do as you wish
# Homepage: http://twhiteman.netfirms.com/Des.html
#
# Modified by: Adam Varga, 2018
#
# A pure python implementation of the DES and Triple DES
# encryption algorithms using CBC mode. Triple DES class is
# implemented by utilising the DES base. Triple DES is
# DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key.
"""
Class initialization
--------------------
pyDes.Des(key, iv)
pyDes.Python_TripleDES(key, iv)
key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes
for Triple DES
iv -> Initialization Vector in bytes. Length must be 8 bytes.
"""
import sys
import warnings
# PY_VER is used to handle Python2 and Python3 differences.
PY_VER = sys.version_info
def new(key, iv):
"""Operate this 3DES cipher."""
return Python_TripleDES(key, iv)
class _baseDes(object):
"""The base class shared by DES and triple DES."""
def __init__(self, iv):
self.iv = iv
def _guard_against_unicode(self, data):
"""Check the data for valid datatype and return them.
Only accept byte strings or ascii unicode values.
Otherwise there is no way to correctly decode the data into bytes.
"""
if PY_VER < (3, ):
if isinstance(data, unicode):
raise ValueError("Only bytes, bytearray or memoryview "
"objects of them should be passed, "
"not Unicode strings")
else:
if isinstance(data, str):
warnings.warn("Only bytes, bytearray or memoryview "
"objects of them should be passed",
DeprecationWarning,
stacklevel=3)
# Only accept ascii unicode values.
try:
return data.encode('ascii')
except UnicodeEncodeError:
raise ValueError("The Unicode string shouldn't be passed")
return data
#############################################
# DES #
#############################################
class Des(_baseDes):
"""DES encryption/decryption class.
Supports CBC (Cypher Block Chaining) mode.
"""
# Permutation and translation tables for DES
__pc1 = [56, 48, 40, 32, 24, 16, 8,
0, 57, 49, 41, 33, 25, 17,
9, 1, 58, 50, 42, 34, 26,
18, 10, 2, 59, 51, 43, 35,
62, 54, 46, 38, 30, 22, 14,
6, 61, 53, 45, 37, 29, 21,
13, 5, 60, 52, 44, 36, 28,
20, 12, 4, 27, 19, 11, 3]
# Number left rotations of pc1
__left_rotations = [1, 1, 2, 2, 2, 2, 2, 2,
1, 2, 2, 2, 2, 2, 2, 1]
# Permuted choice key (table 2)
__pc2 = [13, 16, 10, 23, 0, 4,
2, 27, 14, 5, 20, 9,
22, 18, 11, 3, 25, 7,
15, 6, 26, 19, 12, 1,
40, 51, 30, 36, 46, 54,
29, 39, 50, 44, 32, 47,
43, 48, 38, 55, 33, 52,
45, 41, 49, 35, 28, 31]
# Initial permutation IP
__ip = [57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 19, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7,
56, 48, 40, 32, 24, 16, 8, 0,
58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6]
# Expansion table for turning 32 bit blocks into 48 bits
__expansion_table = [31, 0, 1, 2, 3, 4,
3, 4, 5, 6, 7, 8,
7, 8, 9, 10, 11, 12,
11, 12, 13, 14, 15, 16,
15, 16, 17, 18, 19, 20,
19, 20, 21, 22, 23, 24,
23, 24, 25, 26, 27, 28,
27, 28, 29, 30, 31, 0]
# The (in)famous S-boxes
__sbox = [ # S1
[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],
# S2
[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],
# S3
[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],
# S4
[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],
# S5
[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],
# S6
[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],
# S7
[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],
# S8
[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11], ]
# 32-bit permutation function P used on the output of the S-boxes
__p = [15, 6, 19, 20, 28, 11,
27, 16, 0, 14, 22, 25,
4, 17, 30, 9, 1, 7,
23, 13, 31, 26, 2, 8,
18, 12, 29, 5, 21, 10,
3, 24]
# Final permutation IP^-1
__fp = [39, 7, 47, 15, 55, 23, 63, 31,
38, 6, 46, 14, 54, 22, 62, 30,
37, 5, 45, 13, 53, 21, 61, 29,
36, 4, 44, 12, 52, 20, 60, 28,
35, 3, 43, 11, 51, 19, 59, 27,
34, 2, 42, 10, 50, 18, 58, 26,
33, 1, 41, 9, 49, 17, 57, 25,
32, 0, 40, 8, 48, 16, 56, 24]
# Type of crypting being done
ENCRYPT = 0x00
DECRYPT = 0x01
# Initialisation
def __init__(self, key, iv=None):
# Sanity checking of arguments
if len(key) != 8:
raise ValueError("Invalid DES key size. Key must be exactly "
"8 bytes long")
super(Des, self).__init__(iv)
self.key_size = 8
self._l = []
self._r = []
self._kn = [[0] * 48] * 16 # 16 48-bit keys (K1 - K16)
self._final = []
self.set_key(key)
def set_key(self, key):
"""Set the crypting key for this object. Must be 8 bytes."""
self.key = key
self.__create_sub_keys()
def __string_to_bitlist(self, data):
"""Turn the string data into a list of bits (1, 0)'s."""
if PY_VER < (3, ):
# Turn the strings into integers. Python 3 uses a bytes
# class, which already has this behaviour
if not isinstance(data, bytearray):
data = [ord(c) for c in data]
len_data = len(data) * 8
result = [0] * len_data
pos = 0
for ch in data:
i = 7
while i >= 0:
if ch & (1 << i) != 0:
result[pos] = 1
else:
result[pos] = 0
pos += 1
i -= 1
return result
def __bitlist_to_string(self, data):
"""Turn the data as list of bits into a string."""
result = []
pos = 0
c = 0
while pos < len(data):
c += data[pos] << (7 - (pos % 8))
if (pos % 8) == 7:
result.append(c)
c = 0
pos += 1
if PY_VER < (3, ):
return ''.join([chr(c) for c in result])
else:
return bytes(result)
def __permutate(self, table, block):
"""Permutate this block with the specified table."""
return [block[x] for x in table]
def __create_sub_keys(self):
"""Transform the secret key for data processing.
Create the 16 subkeys k[1] to k[16] from the given key.
"""
key = self.__permutate(Des.__pc1,
self.__string_to_bitlist(self.key))
# Split into Left and Right sections
self._l = key[:28]
self._r = key[28:]
for i in range(16):
# Perform circular left shifts
for _ in range(Des.__left_rotations[i]):
self._l.append(self._l[0])
del self._l[0]
self._r.append(self._r[0])
del self._r[0]
# Create one of the 16 subkeys through pc2 permutation
self._kn[i] = self.__permutate(Des.__pc2, self._l + self._r)
def __des_crypt(self, block, crypt_type):
"""Crypt the block of data through DES bit-manipulation."""
block = self.__permutate(Des.__ip, block)
self._l = block[:32]
self._r = block[32:]
# Encryption starts from _kn[1] through to _kn[16]
if crypt_type == Des.ENCRYPT:
iteration = 0
iteration_adjustment = 1
# Decryption starts from _kn[16] down to _kn[1]
else:
iteration = 15
iteration_adjustment = -1
for _ in range(16):
# Make a copy of _r[i-1], this will later become _l[i]
temp_r = self._r[:]
# Permutate _r[i - 1] to start creating _r[i]
self._r = self.__permutate(Des.__expansion_table, self._r)
# Exclusive or _r[i - 1] with k[i], create b[1] to b[8] whilst here
self._r = [x ^ y for x, y in zip(self._r, self._kn[iteration])]
b = [self._r[:6], self._r[6:12], self._r[12:18], self._r[18:24],
self._r[24:30], self._r[30:36], self._r[36:42], self._r[42:]]
# Permutate b[1] to b[8] using the S-Boxes
bn = [0] * 32
pos = 0
for j in range(8):
# Work out the offsets
m = (b[j][0] << 1) + b[j][5]
n = (b[j][1] << 3) + (b[j][2] << 2) + (b[j][3] << 1) + b[j][4]
# Find the permutation value
v = Des.__sbox[j][(m << 4) + n]
# Turn value into bits, add it to result: bn
bn[pos] = (v & 8) >> 3
bn[pos + 1] = (v & 4) >> 2
bn[pos + 2] = (v & 2) >> 1
bn[pos + 3] = v & 1
pos += 4
# Permutate the concatination of b[1] to b[8] (bn)
self._r = self.__permutate(Des.__p, bn)
# Xor with _l[i - 1]
self._r = [x ^ y for x, y in zip(self._r, self._l)]
self._l = temp_r
iteration += iteration_adjustment
# Final permutation of _r[16]_l[16]
self._final = self.__permutate(Des.__fp, self._r + self._l)
return self._final
def crypt(self, data, crypt_type):
"""Crypt the data in blocks, running it through des_crypt()."""
iv = self.__string_to_bitlist(self.iv)
# Split the data into blocks, crypting each one seperately
i = 0
result = []
while i < len(data):
# Test code for caching encryption results
block = self.__string_to_bitlist(data[i:i+8])
# Xor with iv if using CBC mode
if crypt_type == Des.ENCRYPT:
block = [x ^ y for x, y in zip(block, iv)]
processed_block = self.__des_crypt(block, crypt_type)
if crypt_type == Des.DECRYPT:
processed_block = [x ^ y for x, y in zip(processed_block, iv)]
iv = block
else:
iv = processed_block
# Add the resulting crypted block to our list
result.append(self.__bitlist_to_string(processed_block))
i += 8
# Return the full crypted string
return b''.join(result)
#############################################
# Triple DES #
#############################################
class Python_TripleDES(_baseDes):
"""Triple DES encryption/decrytpion class.
This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or
the DES-EDE2 (when a 16 byte key is supplied) encryption methods.
Supports CBC (Cypher Block Chaining) mode.
"""
def __init__(self, key, iv=None):
self.block_size = 8
if iv:
if len(iv) != self.block_size:
raise ValueError("Invalid Initialization Vector (iv) must be"
" {0} bytes long".format(self.block_size))
iv = self._guard_against_unicode(iv)
else:
raise ValueError("Initialization Vector (iv) must be supplied")
super(Python_TripleDES, self).__init__(iv)
# Will set crypting key for this object. Either 16/24 bytes long.
self.key_size = len(key)
if self.key_size not in (16, 24):
raise ValueError("Invalid triple DES key size. "
"Key must be either 16 or 24 bytes long")
key = self._guard_against_unicode(key)
self.__key1 = Des(key[:8], self.iv)
self.__key2 = Des(key[8:16], self.iv)
if self.key_size == 16:
self.__key3 = Des(key[:8], self.iv)
else:
self.__key3 = Des(key[16:], self.iv)
self.isAEAD = False
self.isBlockCipher = True
self.name = "3des"
self.implementation = "python"
self.__key1.iv = self.iv
self.__key2.iv = self.iv
self.__key3.iv = self.iv
def encrypt(self, data):
"""Encrypt data and return bytes.
data : bytes to be encrypted
The data must be a multiple of 8 bytes and will be encrypted
with the already specified key.
"""
ENCRYPT = Des.ENCRYPT
DECRYPT = Des.DECRYPT
if not data:
return bytearray(b'')
data = self._guard_against_unicode(data)
if len(data) % self.block_size:
raise ValueError("Invalid data length, must be a multiple "
"of {0} bytes".format(self.block_size))
i = 0
result = []
while i < len(data):
block = self.__key1.crypt(data[i:i+8], ENCRYPT)
block = self.__key2.crypt(block, DECRYPT)
block = self.__key3.crypt(block, ENCRYPT)
self.__key1.iv = block
self.__key2.iv = block
self.__key3.iv = block
result.append(block)
i += 8
return bytearray(b''.join(result))
def decrypt(self, data):
"""Decrypt data and return bytes.
data : bytes to be encrypted
The data must be a multiple of 8 bytes and will be decrypted
with the already specified key.
"""
ENCRYPT = Des.ENCRYPT
DECRYPT = Des.DECRYPT
if not data:
return bytearray(b'')
data = self._guard_against_unicode(data)
if len(data) % self.block_size:
raise ValueError("Invalid data length, must be a multiple "
"of {0} bytes".format(self.block_size))
i = 0
result = []
while i < len(data):
iv = data[i:i+8]
block = self.__key3.crypt(iv, DECRYPT)
block = self.__key2.crypt(block, ENCRYPT)
block = self.__key1.crypt(block, DECRYPT)
self.__key1.iv = iv
self.__key2.iv = iv
self.__key3.iv = iv
result.append(block)
i += 8
data = b''.join(result)
return bytearray(data)