diff options
| author | Kévin Le Gouguec <kevin.legouguec@airbus.com> | 2019-03-25 11:01:42 +0100 |
|---|---|---|
| committer | Kévin Le Gouguec <kevin.legouguec@airbus.com> | 2019-03-25 11:01:42 +0100 |
| commit | 24c5f5d817085bd875fa6b86ef261d87b9d5fef4 (patch) | |
| tree | b70624d4c77e1635c9f8f4953a1b46fce8a1631a /src/add_python | |
| parent | 7d08844da485016ce87432a36b397d9919d91f38 (diff) | |
| parent | fc64da017336c553a345fdb690a2e496a4aefff3 (diff) | |
| download | lilliput-ae-implem-24c5f5d817085bd875fa6b86ef261d87b9d5fef4.tar.xz | |
Merge branch 'refactor-python-implem'
Diffstat (limited to 'src/add_python')
| -rw-r--r-- | src/add_python/lilliput/__init__.py | 57 | ||||
| -rw-r--r-- | src/add_python/lilliput/ae_common.py | 104 | ||||
| -rw-r--r-- | src/add_python/lilliput/ae_mode_1.py | 165 | ||||
| -rw-r--r-- | src/add_python/lilliput/ae_mode_2.py | 127 | ||||
| -rw-r--r-- | src/add_python/lilliput/constants.py | 40 | ||||
| -rw-r--r-- | src/add_python/lilliput/helpers.py | 19 | ||||
| -rw-r--r-- | src/add_python/lilliput/multiplications.py | 207 | ||||
| -rw-r--r-- | src/add_python/lilliput/tbc.py | 156 |
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]) |
