diff options
Diffstat (limited to 'src/add_python/lilliput/ae_mode_1.py')
| -rw-r--r-- | src/add_python/lilliput/ae_mode_1.py | 165 |
1 files changed, 165 insertions, 0 deletions
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) |
