ae_common.py 2.41 KB
Newer Older
lwc-tester committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 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