# 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 .helpers import ( ArrayToBlockbytesMatrix, BlockbytesMatrixToBytes, BuildAuth, Padding10LSB, TagValidationError, XorState ) from . import tbc TWEAK_BITS = 128 TWEAK_BYTES = TWEAK_BITS//8 def _TweakTag(j, padded): tweak = [0 for byte in range(0, TWEAK_BYTES)] tweak[TWEAK_BYTES - 1] |= ((j >> 120) & 0xf) for byte in range(TWEAK_BYTES - 2, -1, -1): tweak[byte] = (j >> (8 * byte)) & 0xff if padded: tweak[TWEAK_BYTES - 1] |= 0x40 return tweak def _TweakTagEnd(N): tweak = [0 for byte in range(0, TWEAK_BYTES)] for byte in range(0, TWEAK_BYTES - 1): tweak[byte] = N[byte] tweak[TWEAK_BYTES - 1] = 0x10 return tweak def _AddTagJ(tag, j): array_j = [0 for byte in range(0, TWEAK_BYTES)] for byte in range(0, TWEAK_BYTES): array_j[byte] = (j >> (byte * 8)) xorr = XorState(tag, array_j) xorr[TWEAK_BYTES - 1] |= 0x80 return xorr def _MesssageAuthTag(M, N, Auth, key): l = len(M)//BLOCK_BYTES need_padding = len(M)%BLOCK_BYTES > 0 tag = list(Auth) M = ArrayToBlockbytesMatrix(M) for j in range(0, l): tweak = _TweakTag(j, False) encryption = tbc.encrypt(tweak, key, M[j]) tag = XorState(tag, encryption) if need_padding: tweak = _TweakTag(l, True) m_padded = Padding10LSB(M[l]) encryption = tbc.encrypt(tweak, key, m_padded) tag = XorState(tag, encryption) tweak = _TweakTagEnd(N) encryption = tbc.encrypt(tweak, key, tag) tag = encryption return tag def _MessageEncryption(M, N, tag, key): l = len(M)//BLOCK_BYTES need_padding = len(M)%BLOCK_BYTES > 0 M = ArrayToBlockbytesMatrix(M) C = [] for j in range(0, l): tweak = _AddTagJ(tag, j) padded_nonce = list(N) + [0x00] encryption = tbc.encrypt(tweak, key, padded_nonce) C.append(XorState(M[j], encryption)) if need_padding: tweak = _AddTagJ(tag, l) padded_nonce = list(N) + [0x00] encryption = tbc.encrypt(tweak, key, padded_nonce) C.append(XorState(M[l], encryption)) return C ################################################################################ def encrypt(A, M, N, key): K = list(key) Auth = BuildAuth(TWEAK_BITS, A, K) tag = _MesssageAuthTag(M, N, Auth, K) C = _MessageEncryption(M, N, tag, K) return BlockbytesMatrixToBytes(C), bytes(tag) def decrypt(A, C, N, tag, key): K = list(key) tag = list(tag) M = BlockbytesMatrixToBytes( _MessageEncryption(C, N, tag, K) ) Auth = BuildAuth(TWEAK_BITS, A, K) tag2 = _MesssageAuthTag(M, N, Auth, K) if tag != tag2: raise TagValidationError(tag, tag2) return M