summaryrefslogtreecommitdiff
path: root/src/add_python/lilliput
diff options
context:
space:
mode:
authorKévin Le Gouguec <kevin.legouguec@airbus.com>2019-03-25 11:01:42 +0100
committerKévin Le Gouguec <kevin.legouguec@airbus.com>2019-03-25 11:01:42 +0100
commit24c5f5d817085bd875fa6b86ef261d87b9d5fef4 (patch)
treeb70624d4c77e1635c9f8f4953a1b46fce8a1631a /src/add_python/lilliput
parent7d08844da485016ce87432a36b397d9919d91f38 (diff)
parentfc64da017336c553a345fdb690a2e496a4aefff3 (diff)
downloadlilliput-ae-implem-24c5f5d817085bd875fa6b86ef261d87b9d5fef4.tar.xz
Merge branch 'refactor-python-implem'
Diffstat (limited to 'src/add_python/lilliput')
-rw-r--r--src/add_python/lilliput/__init__.py57
-rw-r--r--src/add_python/lilliput/ae_common.py104
-rw-r--r--src/add_python/lilliput/ae_mode_1.py165
-rw-r--r--src/add_python/lilliput/ae_mode_2.py127
-rw-r--r--src/add_python/lilliput/constants.py40
-rw-r--r--src/add_python/lilliput/helpers.py19
-rw-r--r--src/add_python/lilliput/multiplications.py207
-rw-r--r--src/add_python/lilliput/tbc.py156
8 files changed, 875 insertions, 0 deletions
diff --git a/src/add_python/lilliput/__init__.py b/src/add_python/lilliput/__init__.py
new file mode 100644
index 0000000..870e485
--- /dev/null
+++ b/src/add_python/lilliput/__init__.py
@@ -0,0 +1,57 @@
+# Implementation of the Lilliput-AE tweakable block cipher.
+#
+# Authors, hereby denoted as "the implementer":
+# Kévin Le Gouguec,
+# Léo Reynaud
+# 2019.
+#
+# For more information, feedback or questions, refer to our website:
+# https://paclido.fr/lilliput-ae
+#
+# To the extent possible under law, the implementer has waived all copyright
+# and related or neighboring rights to the source code in this file.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+"""Lilliput-AE tweakable block cipher.
+
+This module provides the high-level functions for authenticated encryption and
+decryption. Both functions take and return bytestring values.
+
+The "mode" argument can be either of the following integers:
+
+- 1, for the ΘCB3 nonce-respecting mode,
+- 2, for the SCT-2 nonce-misuse-resistant mode.
+"""
+
+
+from . import ae_mode_1
+from . import ae_mode_2
+from .constants import NONCE_BITS
+
+
+_AE_MODES = {
+ 1: ae_mode_1,
+ 2: ae_mode_2
+}
+
+
+def _check_inputs(key, mode, nonce):
+ valid_key_lengths = (128, 192, 256)
+ if len(key)*8 not in valid_key_lengths:
+ raise ValueError('invalid key size: {} not in {}'.format(len(key)*8, valid_key_lengths))
+
+ if mode not in _AE_MODES:
+ raise ValueError('invalid mode: {} not in {}'.format(mode, tuple(_AE_MODES)))
+
+ if len(nonce)*8 != NONCE_BITS:
+ raise ValueError('invalid nonce size: expecting {}, have {}'.format(NONCE_BITS, len(nonce)*8))
+
+
+def encrypt(plaintext, adata, key, nonce, mode):
+ _check_inputs(key, mode, nonce)
+ return _AE_MODES[mode].encrypt(adata, plaintext, nonce, key)
+
+
+def decrypt(ciphertext, tag, adata, key, nonce, mode):
+ _check_inputs(key, mode, nonce)
+ return _AE_MODES[mode].decrypt(adata, ciphertext, nonce, tag, key)
diff --git a/src/add_python/lilliput/ae_common.py b/src/add_python/lilliput/ae_common.py
new file mode 100644
index 0000000..b94be1b
--- /dev/null
+++ b/src/add_python/lilliput/ae_common.py
@@ -0,0 +1,104 @@
+# Implementation of the Lilliput-AE tweakable block cipher.
+#
+# Authors, hereby denoted as "the implementer":
+# Kévin Le Gouguec,
+# Léo Reynaud
+# 2019.
+#
+# For more information, feedback or questions, refer to our website:
+# https://paclido.fr/lilliput-ae
+#
+# To the extent possible under law, the implementer has waived all copyright
+# and related or neighboring rights to the source code in this file.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+"""Helper functions used in both Lilliput-I and Lilliput-II."""
+
+
+from .constants import BLOCK_BITS, BLOCK_BYTES
+from .helpers import xor
+from . import tbc
+
+
+def bytes_to_block_matrix(array):
+ vector = list(array)
+
+ blocks_nb = len(vector)//BLOCK_BYTES
+
+ block_starts = (
+ i*BLOCK_BYTES for i in range(blocks_nb)
+ )
+
+ matrix = [
+ vector[start:start+BLOCK_BYTES] for start in block_starts
+ ]
+
+ padding_len = len(vector)%BLOCK_BYTES
+
+ if padding_len > 0:
+ padding = vector[-padding_len:]
+ matrix.append(padding)
+
+ return matrix
+
+
+def block_matrix_to_bytes(matrix):
+ return bytes(byte for block in matrix for byte in block)
+
+
+def pad10(X):
+ zeroes = [0] * (BLOCK_BYTES-len(X)-1)
+ return zeroes + [0b10000000] + X
+
+
+def integer_to_byte_array(i, n):
+ return list(i.to_bytes(n, 'little'))
+
+
+def _tweak_associated_data(t, i, padded):
+ tweak = integer_to_byte_array(i, t//8)
+
+ prefix = 0b0110 if padded else 0b0010
+
+ # Clear upper 4 bits and set them to prefix.
+ tweak[-1] &= 0b00001111
+ tweak[-1] = prefix << 4
+
+ return tweak
+
+
+def build_auth(t, A, key):
+ Auth = [0]*BLOCK_BYTES
+
+ l_a = len(A)//BLOCK_BYTES
+ need_padding = len(A)%BLOCK_BYTES > 0
+
+ A = bytes_to_block_matrix(A)
+
+ for i in range(l_a):
+ tweak = _tweak_associated_data(t, i, padded=False)
+ enc = tbc.encrypt(tweak, key, A[i])
+ Auth = xor(Auth, enc)
+
+ if not need_padding:
+ return Auth
+
+ tweak = _tweak_associated_data(t, l_a, padded=True)
+ ad_padded = pad10(A[l_a])
+ enc = tbc.encrypt(tweak, key, ad_padded)
+ Auth = xor(Auth, enc)
+
+ return Auth
+
+
+class TagValidationError(Exception):
+ def __init__(self, announced, computed):
+ msg = '\n'.join((
+ 'Invalid tag:',
+ announced.hex().upper()+' (announced)',
+ computed.hex().upper()+' (computed)'
+ ))
+
+ super().__init__(msg)
+ self._announced = announced
+ self._computed = computed
diff --git a/src/add_python/lilliput/ae_mode_1.py b/src/add_python/lilliput/ae_mode_1.py
new file mode 100644
index 0000000..4a40b78
--- /dev/null
+++ b/src/add_python/lilliput/ae_mode_1.py
@@ -0,0 +1,165 @@
+# Implementation of the Lilliput-AE tweakable block cipher.
+#
+# Authors, hereby denoted as "the implementer":
+# Kévin Le Gouguec,
+# Léo Reynaud
+# 2019.
+#
+# For more information, feedback or questions, refer to our website:
+# https://paclido.fr/lilliput-ae
+#
+# To the extent possible under law, the implementer has waived all copyright
+# and related or neighboring rights to the source code in this file.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+"""Lilliput-I Authenticated Encryption mode.
+
+This module provides the functions for authenticated encryption and decryption
+using Lilliput-AE's nonce-respecting mode based on ΘCB3.
+"""
+
+from enum import Enum
+
+from .constants import BLOCK_BYTES, NONCE_BITS
+from .ae_common import (
+ bytes_to_block_matrix,
+ block_matrix_to_bytes,
+ build_auth,
+ integer_to_byte_array,
+ pad10,
+ TagValidationError,
+ xor
+)
+from . import tbc
+
+
+TWEAK_BITS = 192
+TWEAK_BYTES = TWEAK_BITS//8
+
+
+class _MessageTweak(Enum):
+ BLOCK = 0b0000
+ NO_PADDING = 0b0001
+ PAD = 0b0100
+ FINAL = 0b0101
+
+
+def _upper_nibble(i):
+ return i >> 4
+
+
+def _lower_nibble(i):
+ return i & 0b00001111
+
+
+def _byte_from_nibbles(lower, upper):
+ return upper<<4 | lower
+
+
+def _tweak_message(N, j, prefix):
+ # j is encoded on 68 bits; get 72 and clear the upper 4.
+ j_len = (TWEAK_BITS-NONCE_BITS-4)//8 + 1
+ tweak = integer_to_byte_array(j, j_len)
+ tweak[-1] &= 0b00001111
+
+ # Add nonce.
+ tweak[-1] |= _lower_nibble(N[0]) << 4
+ tweak.extend(
+ _byte_from_nibbles(_upper_nibble(N[i-1]), _lower_nibble(N[i]))
+ for i in range(1, NONCE_BITS//8)
+ )
+
+ # Add last nibble from nonce and prefix.
+ tweak.append(
+ _byte_from_nibbles(_upper_nibble(N[-1]), prefix.value)
+ )
+
+ return tweak
+
+
+def _treat_message_enc(M, N, key):
+ checksum = [0]*BLOCK_BYTES
+
+ l = len(M)//BLOCK_BYTES
+ padding_bytes = len(M)%BLOCK_BYTES
+
+ M = bytes_to_block_matrix(M)
+ C = []
+
+ for j in range(0, l):
+ checksum = xor(checksum, M[j])
+ tweak = _tweak_message(N, j, _MessageTweak.BLOCK)
+ C.append(tbc.encrypt(tweak, key, M[j]))
+
+ if padding_bytes == 0:
+ tweak = _tweak_message(N, l, _MessageTweak.NO_PADDING)
+ Final = tbc.encrypt(tweak, key, checksum)
+
+ else:
+ m_padded = pad10(M[l])
+ checksum = xor(checksum, m_padded)
+ tweak = _tweak_message(N, l, _MessageTweak.PAD)
+ pad = tbc.encrypt(tweak, key, [0]*BLOCK_BYTES)
+
+ C.append(xor(M[l], pad[:padding_bytes]))
+ tweak_final = _tweak_message(N, l+1, _MessageTweak.FINAL)
+ Final = tbc.encrypt(tweak_final, key, checksum)
+
+ return Final, C
+
+
+def _treat_message_dec(C, N, key):
+ checksum = [0]*BLOCK_BYTES
+
+ l = len(C)//BLOCK_BYTES
+ padding_bytes = len(C)%BLOCK_BYTES
+
+ C = bytes_to_block_matrix(C)
+ M = []
+
+ for j in range(0, l):
+ tweak = _tweak_message(N, j, _MessageTweak.BLOCK)
+ M.append(tbc.decrypt(tweak, key, C[j]))
+ checksum = xor(checksum, M[j])
+
+ if padding_bytes == 0:
+ tweak = _tweak_message(N, l, _MessageTweak.NO_PADDING)
+ Final = tbc.encrypt(tweak, key, checksum)
+
+ else:
+ tweak = _tweak_message(N, l, _MessageTweak.PAD)
+ pad = tbc.encrypt(tweak, key, [0]*BLOCK_BYTES)
+ M.append(xor(C[l], pad[:padding_bytes]))
+
+ m_padded = pad10(M[l])
+ checksum = xor(checksum, m_padded)
+ tweak_final = _tweak_message(N, l+1, _MessageTweak.FINAL)
+ Final = tbc.encrypt(tweak_final, key, checksum)
+
+ return Final, M
+
+
+def encrypt(A, M, N, key):
+ K = list(key)
+ N = list(N)
+
+ Auth = build_auth(TWEAK_BITS, A, K)
+ Final, C = _treat_message_enc(M, N, K)
+ tag = xor(Auth, Final)
+
+ return block_matrix_to_bytes(C), bytes(tag)
+
+
+def decrypt(A, C, N, tag, key):
+ K = list(key)
+ N = list(N)
+ tag = list(tag)
+
+ Auth = build_auth(TWEAK_BITS, A, K)
+ Final, M = _treat_message_dec(C, N, K)
+ tag2 = xor(Auth, Final)
+
+ if tag != tag2:
+ raise TagValidationError(tag, tag2)
+
+ return block_matrix_to_bytes(M)
diff --git a/src/add_python/lilliput/ae_mode_2.py b/src/add_python/lilliput/ae_mode_2.py
new file mode 100644
index 0000000..79d1bcd
--- /dev/null
+++ b/src/add_python/lilliput/ae_mode_2.py
@@ -0,0 +1,127 @@
+# Implementation of the Lilliput-AE tweakable block cipher.
+#
+# Authors, hereby denoted as "the implementer":
+# Kévin Le Gouguec,
+# Léo Reynaud
+# 2019.
+#
+# For more information, feedback or questions, refer to our website:
+# https://paclido.fr/lilliput-ae
+#
+# To the extent possible under law, the implementer has waived all copyright
+# and related or neighboring rights to the source code in this file.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+"""Lilliput-II Authenticated Encryption mode.
+
+This module provides the functions for authenticated encryption and decryption
+using Lilliput-AE's nonce-misuse-resistant mode based on SCT-2.
+"""
+
+from .constants import BLOCK_BYTES
+from .ae_common import (
+ bytes_to_block_matrix,
+ block_matrix_to_bytes,
+ integer_to_byte_array,
+ build_auth,
+ pad10,
+ TagValidationError,
+ xor
+)
+from . import tbc
+
+
+TWEAK_BITS = 128
+TWEAK_BYTES = TWEAK_BITS//8
+
+
+def _tweak_tag(j, padded):
+ tweak = integer_to_byte_array(j, TWEAK_BYTES)
+
+ prefix = 0b0100 if padded else 0b0000
+
+ # Clear upper 4 bits and set them to prefix.
+ tweak[-1] &= 0b00001111
+ tweak[-1] = prefix << 4
+
+ return tweak
+
+
+def _add_tag_j(tag, j):
+ array_j = integer_to_byte_array(j, TWEAK_BYTES)
+ tweak = xor(tag, array_j)
+ tweak[-1] |= 0b10000000
+
+ return tweak
+
+
+def _message_auth_tag(M, N, Auth, key):
+ l = len(M)//BLOCK_BYTES
+ need_padding = len(M)%BLOCK_BYTES > 0
+
+ tag = list(Auth)
+ M = bytes_to_block_matrix(M)
+
+ for j in range(0, l):
+ tweak = _tweak_tag(j, False)
+ encryption = tbc.encrypt(tweak, key, M[j])
+ tag = xor(tag, encryption)
+
+ if need_padding:
+ tweak = _tweak_tag(l, True)
+ encryption = tbc.encrypt(tweak, key, pad10(M[l]))
+ tag = xor(tag, encryption)
+
+ tweak = N + [0b00010000]
+ encryption = tbc.encrypt(tweak, key, tag)
+ tag = encryption
+
+ return tag
+
+
+def _message_encryption(M, N, tag, key):
+ l = len(M)//BLOCK_BYTES
+ need_padding = len(M)%BLOCK_BYTES > 0
+
+ M = bytes_to_block_matrix(M)
+ C = []
+
+ for j in range(0, l):
+ tweak = _add_tag_j(tag, j)
+ encryption = tbc.encrypt(tweak, key, N+[0b00000000])
+ C.append(xor(M[j], encryption))
+
+ if need_padding:
+ tweak = _add_tag_j(tag, l)
+ encryption = tbc.encrypt(tweak, key, N+[0b00000000])
+ C.append(xor(M[l], encryption))
+
+ return C
+
+
+def encrypt(A, M, N, key):
+ K = list(key)
+ N = list(N)
+
+ Auth = build_auth(TWEAK_BITS, A, K)
+ tag = _message_auth_tag(M, N, Auth, K)
+ C = _message_encryption(M, N, tag, K)
+
+ return block_matrix_to_bytes(C), bytes(tag)
+
+
+def decrypt(A, C, N, tag, key):
+ K = list(key)
+ N = list(N)
+ tag = list(tag)
+
+ M = block_matrix_to_bytes(
+ _message_encryption(C, N, tag, K)
+ )
+ Auth = build_auth(TWEAK_BITS, A, K)
+ tag2 = _message_auth_tag(M, N, Auth, K)
+
+ if tag != tag2:
+ raise TagValidationError(tag, tag2)
+
+ return M
diff --git a/src/add_python/lilliput/constants.py b/src/add_python/lilliput/constants.py
new file mode 100644
index 0000000..e69ca46
--- /dev/null
+++ b/src/add_python/lilliput/constants.py
@@ -0,0 +1,40 @@
+BLOCK_BITS = 128
+BLOCK_BYTES = BLOCK_BITS//8
+NONCE_BITS = 120
+TAG_BYTES = 16
+
+
+SBOX = [
+ 0x20, 0x00, 0xb2, 0x85, 0x3b, 0x35, 0xa6, 0xa4,
+ 0x30, 0xe4, 0x6a, 0x2c, 0xff, 0x59, 0xe2, 0x0e,
+ 0xf8, 0x1e, 0x7a, 0x80, 0x15, 0xbd, 0x3e, 0xb1,
+ 0xe8, 0xf3, 0xa2, 0xc2, 0xda, 0x51, 0x2a, 0x10,
+ 0x21, 0x01, 0x23, 0x78, 0x5c, 0x24, 0x27, 0xb5,
+ 0x37, 0xc7, 0x2b, 0x1f, 0xae, 0x0a, 0x77, 0x5f,
+ 0x6f, 0x09, 0x9d, 0x81, 0x04, 0x5a, 0x29, 0xdc,
+ 0x39, 0x9c, 0x05, 0x57, 0x97, 0x74, 0x79, 0x17,
+ 0x44, 0xc6, 0xe6, 0xe9, 0xdd, 0x41, 0xf2, 0x8a,
+ 0x54, 0xca, 0x6e, 0x4a, 0xe1, 0xad, 0xb6, 0x88,
+ 0x1c, 0x98, 0x7e, 0xce, 0x63, 0x49, 0x3a, 0x5d,
+ 0x0c, 0xef, 0xf6, 0x34, 0x56, 0x25, 0x2e, 0xd6,
+ 0x67, 0x75, 0x55, 0x76, 0xb8, 0xd2, 0x61, 0xd9,
+ 0x71, 0x8b, 0xcd, 0x0b, 0x72, 0x6c, 0x31, 0x4b,
+ 0x69, 0xfd, 0x7b, 0x6d, 0x60, 0x3c, 0x2f, 0x62,
+ 0x3f, 0x22, 0x73, 0x13, 0xc9, 0x82, 0x7f, 0x53,
+ 0x32, 0x12, 0xa0, 0x7c, 0x02, 0x87, 0x84, 0x86,
+ 0x93, 0x4e, 0x68, 0x46, 0x8d, 0xc3, 0xdb, 0xec,
+ 0x9b, 0xb7, 0x89, 0x92, 0xa7, 0xbe, 0x3d, 0xd8,
+ 0xea, 0x50, 0x91, 0xf1, 0x33, 0x38, 0xe0, 0xa9,
+ 0xa3, 0x83, 0xa1, 0x1b, 0xcf, 0x06, 0x95, 0x07,
+ 0x9e, 0xed, 0xb9, 0xf5, 0x4c, 0xc0, 0xf4, 0x2d,
+ 0x16, 0xfa, 0xb4, 0x03, 0x26, 0xb3, 0x90, 0x4f,
+ 0xab, 0x65, 0xfc, 0xfe, 0x14, 0xf7, 0xe3, 0x94,
+ 0xee, 0xac, 0x8c, 0x1a, 0xde, 0xcb, 0x28, 0x40,
+ 0x7d, 0xc8, 0xc4, 0x48, 0x6b, 0xdf, 0xa5, 0x52,
+ 0xe5, 0xfb, 0xd7, 0x64, 0xf9, 0xf0, 0xd3, 0x5e,
+ 0x66, 0x96, 0x8f, 0x1d, 0x45, 0x36, 0xcc, 0xc5,
+ 0x4d, 0x9f, 0xbf, 0x0f, 0xd1, 0x08, 0xeb, 0x43,
+ 0x42, 0x19, 0xe7, 0x99, 0xa8, 0x8e, 0x58, 0xc1,
+ 0x9a, 0xd4, 0x18, 0x47, 0xaa, 0xaf, 0xbc, 0x5b,
+ 0xd5, 0x11, 0xd0, 0xb0, 0x70, 0xbb, 0x0d, 0xba
+]
diff --git a/src/add_python/lilliput/helpers.py b/src/add_python/lilliput/helpers.py
new file mode 100644
index 0000000..41f75a6
--- /dev/null
+++ b/src/add_python/lilliput/helpers.py
@@ -0,0 +1,19 @@
+# Implementation of the Lilliput-AE tweakable block cipher.
+#
+# Authors, hereby denoted as "the implementer":
+# Kévin Le Gouguec,
+# Léo Reynaud
+# 2019.
+#
+# For more information, feedback or questions, refer to our website:
+# https://paclido.fr/lilliput-ae
+#
+# To the extent possible under law, the implementer has waived all copyright
+# and related or neighboring rights to the source code in this file.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+"""Helper functions used in Lilliput-AE."""
+
+
+def xor(array1, array2):
+ return [a1^a2 for (a1, a2) in zip(array1, array2)]
diff --git a/src/add_python/lilliput/multiplications.py b/src/add_python/lilliput/multiplications.py
new file mode 100644
index 0000000..2dea948
--- /dev/null
+++ b/src/add_python/lilliput/multiplications.py
@@ -0,0 +1,207 @@
+# Implementation of the Lilliput-AE tweakable block cipher.
+#
+# Authors, hereby denoted as "the implementer":
+# Kévin Le Gouguec,
+# Léo Reynaud
+# 2019.
+#
+# For more information, feedback or questions, refer to our website:
+# https://paclido.fr/lilliput-ae
+#
+# To the extent possible under law, the implementer has waived all copyright
+# and related or neighboring rights to the source code in this file.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+"""Multiplications for Lilliput-TBC's tweakey schedule.
+
+This module provides a list of functions implementing lane multiplications,
+from ALPHAS[0] = α₀ = I to ALPHAS[6] = α₆ = M_R³.
+"""
+
+
+def _multiply_M(lane):
+ multiplied_lane = [lane[(byte-1) % 8] for byte in range(0, 8)]
+
+ multiplied_lane[2] ^= ((lane[6] << 2) & 0xff)
+ multiplied_lane[4] ^= ((lane[4] >> 3) & 0xff)
+ multiplied_lane[5] ^= ((lane[5] << 3) & 0xff)
+
+ return multiplied_lane
+
+
+def _multiply_M2(lane):
+ multiplied_lane = [lane[(byte-2) % 8] for byte in range(0, 8)]
+
+ multiplied_lane[2] ^= ((lane[5] << 2) & 0xff)
+ multiplied_lane[3] ^= ((lane[6] << 2) & 0xff)
+ multiplied_lane[4] ^= ((lane[3] >> 3) & 0xff) ^ ((lane[4] >> 6) & 0xff)
+ multiplied_lane[5] ^= ((lane[5] << 6) & 0xff)
+ multiplied_lane[6] ^= ((lane[5] << 3) & 0xff)
+
+ # binary matrix M1
+ multi_mat_l4_m1 = 0
+ l4 = lane[4]
+ multi_mat_l4_m1 ^= ((l4 & 0x8) >> 3)
+ multi_mat_l4_m1 ^= ((l4 & 0x10) >> 3)
+ multi_mat_l4_m1 ^= ((l4 & 0x20) >> 3)
+ multi_mat_l4_m1 ^= ((l4 & 0x40) >> 3) ^ ((l4 & 0x1) << 3)
+ multi_mat_l4_m1 ^= ((l4 & 0x80) >> 3) ^ ((l4 & 0x2) << 3)
+ multi_mat_l4_m1 ^= ((l4 & 0x04) << 3)
+ multi_mat_l4_m1 ^= ((l4 & 0x08) << 3)
+ multi_mat_l4_m1 ^= ((l4 & 0x10) << 3)
+
+ multiplied_lane[5] ^= multi_mat_l4_m1
+
+ return multiplied_lane
+
+
+def _multiply_M3(lane):
+ multiplied_lane = [lane[(byte-3) % 8] for byte in range(0, 8)]
+
+ multiplied_lane[2] ^= ((lane[4] << 2) & 0xff) ^ ((lane[5] << 5) & 0xff)
+ multiplied_lane[3] ^= ((lane[5] << 2) & 0xff)
+ multiplied_lane[4] ^= ((lane[2] >> 3) & 0xff) ^ ((lane[3] >> 6) & 0xff) ^ ((lane[6] << 2) & 0xff)
+ multiplied_lane[6] ^= ((lane[5] << 6) & 0xff)
+ multiplied_lane[7] ^= ((lane[5] << 3) & 0xff)
+
+ # binary matrix M1
+ multi_mat_l3_m1 = 0
+ l3 = lane[3]
+ multi_mat_l3_m1 ^= ((l3 & 0x8) >> 3)
+ multi_mat_l3_m1 ^= ((l3 & 0x10) >> 3)
+ multi_mat_l3_m1 ^= ((l3 & 0x20) >> 3)
+ multi_mat_l3_m1 ^= ((l3 & 0x40) >> 3) ^ ((l3 & 0x1) << 3)
+ multi_mat_l3_m1 ^= ((l3 & 0x80) >> 3) ^ ((l3 & 0x2) << 3)
+ multi_mat_l3_m1 ^= ((l3 & 0x04) << 3)
+ multi_mat_l3_m1 ^= ((l3 & 0x08) << 3)
+ multi_mat_l3_m1 ^= ((l3 & 0x10) << 3)
+
+ # binary matrix M1
+ multi_mat_l4_m1 = 0
+ l4 = lane[4]
+ multi_mat_l4_m1 ^= ((l4 & 0x8) >> 3)
+ multi_mat_l4_m1 ^= ((l4 & 0x10) >> 3)
+ multi_mat_l4_m1 ^= ((l4 & 0x20) >> 3)
+ multi_mat_l4_m1 ^= ((l4 & 0x40) >> 3) ^ ((l4 & 0x1) << 3)
+ multi_mat_l4_m1 ^= ((l4 & 0x80) >> 3) ^ ((l4 & 0x2) << 3)
+ multi_mat_l4_m1 ^= ((l4 & 0x04) << 3)
+ multi_mat_l4_m1 ^= ((l4 & 0x08) << 3)
+ multi_mat_l4_m1 ^= ((l4 & 0x10) << 3)
+
+ # binary matrix M2
+ multi_mat_l4_m2 = 0
+ l4 = lane[4]
+ multi_mat_l4_m2 ^= ((l4 & 0x40) >> 6)
+ multi_mat_l4_m2 ^= ((l4 & 0x80) >> 6)
+ multi_mat_l4_m2 ^= (l4 & 0x08)
+ multi_mat_l4_m2 ^= (l4 & 0x10)
+ multi_mat_l4_m2 ^= (l4 & 0x20)
+ multi_mat_l4_m2 ^= (l4 & 0x40) ^ ((l4 & 0x1) << 6)
+ multi_mat_l4_m2 ^= (l4 & 0x80) ^ ((l4 & 0x2) << 6)
+
+
+ multiplied_lane[5] ^= multi_mat_l3_m1 ^ multi_mat_l4_m2
+ multiplied_lane[6] ^= multi_mat_l4_m1
+
+ return multiplied_lane
+
+
+def _multiply_MR(lane):
+ multiplied_lane = [lane[(byte+1) % 8] for byte in range(0, 8)]
+
+ multiplied_lane[2] ^= ((lane[4] >> 3) & 0xff)
+ multiplied_lane[4] ^= ((lane[6] << 3) & 0xff)
+ multiplied_lane[5] ^= ((lane[3] << 2) & 0xff)
+
+ return multiplied_lane
+
+
+def _multiply_MR2(lane):
+ multiplied_lane = [lane[(byte+2) % 8] for byte in range(0, 8)]
+
+ multiplied_lane[1] ^= ((lane[4] >> 3) & 0xff)
+ multiplied_lane[2] ^= ((lane[5] >> 3) & 0xff)
+ multiplied_lane[3] ^= ((lane[6] << 3) & 0xff)
+ multiplied_lane[4] ^= ((lane[3] << 2) & 0xff) ^ ((lane[7] << 3) & 0xff)
+ multiplied_lane[5] ^= ((lane[4] << 2) & 0xff)
+
+
+ # binary matrix m3
+ multi_mat_l6_m3 = 0
+ l6 = lane[6]
+ multi_mat_l6_m3 ^= (l6 & 0x1)
+ multi_mat_l6_m3 ^= (l6 & 0x2)
+ multi_mat_l6_m3 ^= (l6 & 0x4)
+ multi_mat_l6_m3 ^= (l6 & 0x8)
+ multi_mat_l6_m3 ^= (l6 & 0x10)
+
+
+ multiplied_lane[2] ^= multi_mat_l6_m3
+
+ return multiplied_lane
+
+
+def _multiply_MR3(lane):
+ multiplied_lane = [lane[(byte+3) % 8] for byte in range(0, 8)]
+
+ multiplied_lane[0] ^= ((lane[4] >> 3) & 0xff)
+ multiplied_lane[1] ^= ((lane[5] >> 3) & 0xff)
+ multiplied_lane[3] ^= ((lane[3] << 2) & 0xff) ^ ((lane[7] << 3) & 0xff)
+ multiplied_lane[4] ^= ((lane[0] << 3) & 0xff) ^ ((lane[4] << 2) & 0xff)
+ multiplied_lane[5] ^= ((lane[5] << 2) & 0xff) ^ ((lane[6] << 5) & 0xff)
+
+ # binary matrix m3
+ multi_mat_l6_m3 = 0
+ l6 = lane[6]
+ multi_mat_l6_m3 ^= (l6 & 0x1)
+ multi_mat_l6_m3 ^= (l6 & 0x2)
+ multi_mat_l6_m3 ^= (l6 & 0x4)
+ multi_mat_l6_m3 ^= (l6 & 0x8)
+ multi_mat_l6_m3 ^= (l6 & 0x10)
+
+ # binary matrix m3
+ multi_mat_l7_m3 = 0
+ l7 = lane[7]
+ multi_mat_l7_m3 ^= (l7 & 0x1)
+ multi_mat_l7_m3 ^= (l7 & 0x2)
+ multi_mat_l7_m3 ^= (l7 & 0x4)
+ multi_mat_l7_m3 ^= (l7 & 0x8)
+ multi_mat_l7_m3 ^= (l7 & 0x10)
+
+ # binary matrix m4
+ multi_mat_l3_m4 = 0
+ l3 = lane[3]
+ multi_mat_l3_m4 ^= ((l3 & 0x2) >> 1)
+ multi_mat_l3_m4 ^= ((l3 & 0x4) >> 1)
+ multi_mat_l3_m4 ^= ((l3 & 0x8) >> 1)
+ multi_mat_l3_m4 ^= ((l3 & 0x10) >> 1)
+ multi_mat_l3_m4 ^= ((l3 & 0x20) >> 1)
+
+ # binary matrix m1 for MR
+ multi_mat_l6_m1 = 0
+ l6 = lane[6]
+ multi_mat_l6_m1 ^= ((l6 & 0x8) >> 3)
+ multi_mat_l6_m1 ^= ((l6 & 0x10) >> 3)
+ multi_mat_l6_m1 ^= ((l6 & 0x20) >> 3)
+ multi_mat_l6_m1 ^= ((l6 & 0x40) >> 3) ^ ((l6 & 0x1) << 3)
+ multi_mat_l6_m1 ^= ((l6 & 0x80) >> 3) ^ ((l6 & 0x2) << 3)
+ multi_mat_l6_m1 ^= ((l6 & 0x4) << 3)
+ multi_mat_l6_m1 ^= ((l6 & 0x8) << 3)
+ multi_mat_l6_m1 ^= ((l6 & 0x10) << 3)
+
+
+ multiplied_lane[1] ^= multi_mat_l6_m3
+ multiplied_lane[2] ^= multi_mat_l3_m4 ^ multi_mat_l6_m1 ^ multi_mat_l7_m3
+
+ return multiplied_lane
+
+
+ALPHAS = (
+ list, # Identity.
+ _multiply_M,
+ _multiply_M2,
+ _multiply_M3,
+ _multiply_MR,
+ _multiply_MR2,
+ _multiply_MR3
+)
diff --git a/src/add_python/lilliput/tbc.py b/src/add_python/lilliput/tbc.py
new file mode 100644
index 0000000..0772853
--- /dev/null
+++ b/src/add_python/lilliput/tbc.py
@@ -0,0 +1,156 @@
+# Implementation of the Lilliput-AE tweakable block cipher.
+#
+# Authors, hereby denoted as "the implementer":
+# Kévin Le Gouguec,
+# Léo Reynaud
+# 2019.
+#
+# For more information, feedback or questions, refer to our website:
+# https://paclido.fr/lilliput-ae
+#
+# To the extent possible under law, the implementer has waived all copyright
+# and related or neighboring rights to the source code in this file.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+"""Lilliput-TBC tweakable block cipher.
+
+This module provides functions to encrypt and decrypt blocks of 128 bits.
+"""
+
+from .constants import BLOCK_BYTES, SBOX
+from .helpers import xor
+from .multiplications import ALPHAS
+
+
+_PERMUTATION = [14, 11, 12, 10, 8, 9, 13, 15, 3, 1, 4, 5, 6, 0, 2, 7]
+_PERMUTATION_INV = [13, 9, 14, 8, 10, 11, 12, 15, 4, 5, 3, 1, 2, 6 ,0 ,7]
+
+
+_ROUNDS = {
+ 128: 32,
+ 192: 36,
+ 256: 42
+}
+
+
+def _build_tweakey(tweak, key):
+ return tweak+key
+
+
+def _lane(TK, j):
+ return TK[j*8:(j+1)*8]
+
+
+def _round_tweakey_schedule(tweakey):
+ p = len(tweakey)//8
+
+ multiplied_lanes = (
+ ALPHAS[j](_lane(tweakey, j)) for j in range(p)
+ )
+
+ return [byte for lane in multiplied_lanes for byte in lane]
+
+
+def _subtweakey_extract(tweakey, Ci):
+ RTKi = [0]*8
+
+ for j, byte in enumerate(tweakey):
+ RTKi[j%8] ^= byte
+
+ RTKi[0] ^= Ci
+
+ return RTKi
+
+
+def _tweakey_schedule_whole(tweakey, r):
+ TKs = [tweakey]
+ RTKs = [_subtweakey_extract(TKs[0], 0)]
+
+ for i in range(1, r):
+ TKs.append(_round_tweakey_schedule(TKs[i-1]))
+ RTKs.append(_subtweakey_extract(TKs[i], i))
+
+ return RTKs
+
+
+def _non_linear_layer(state, subtweakey):
+ variables_xored = xor(state, subtweakey)
+
+ variables_sboxed = [
+ SBOX[variables_xored[i]] for i in range(8)
+ ]
+
+ state_output = state
+ for i in range(8):
+ state_output[15-i] ^= variables_sboxed[i]
+
+ return state_output
+
+
+def _linear_layer(state):
+ state_output = state
+
+ for byte in range(1, 8):
+ state_output[15] ^= state[byte]
+
+ for byte in range(9, 15):
+ state_output[byte] ^= state[7]
+
+ return state_output
+
+
+def _permutation_layer(state, p):
+ return [
+ state[p[i]] for i in range(BLOCK_BYTES)
+ ]
+
+
+def _one_round_egfn_enc(state, subtweakey):
+ state_non_linear = _non_linear_layer(state, subtweakey)
+ state_linear = _linear_layer(state_non_linear)
+ state_permutation = _permutation_layer(state_linear, _PERMUTATION)
+
+ return state_permutation
+
+
+def _last_round_egfn(state, subtweakey):
+ state_non_linear = _non_linear_layer(state, subtweakey)
+ state_linear = _linear_layer(state_non_linear)
+
+ return state_linear
+
+
+def _one_round_egfn_dec(state, subtweakey):
+ state_non_linear = _non_linear_layer(state, subtweakey)
+ state_linear = _linear_layer(state_non_linear)
+ state_permutation = _permutation_layer(state_linear, _PERMUTATION_INV)
+
+ return state_permutation
+
+
+def encrypt(tweak, key, message):
+ r = _ROUNDS[8*len(key)]
+
+ tweakey = _build_tweakey(tweak, key)
+ RTKs = _tweakey_schedule_whole(tweakey, r)
+
+ state = message
+
+ for i in range(r-1):
+ state = _one_round_egfn_enc(state, RTKs[i])
+
+ return _last_round_egfn(state, RTKs[r-1])
+
+
+def decrypt(tweak, key, cipher):
+ r = _ROUNDS[8*len(key)]
+
+ tweakey = _build_tweakey(tweak, key)
+ RTKs = _tweakey_schedule_whole(tweakey, r)
+
+ state = cipher
+
+ for i in range(r-1):
+ state = _one_round_egfn_dec(state, RTKs[r-i-1])
+
+ return _last_round_egfn(state, RTKs[0])