lilliput-ae-reference-implementation

Implementations of Lilliput-AE submitted to the NIST LWC standardization process
git clone https://git.kevinlegouguec.net/lilliput-ae-reference-implementation
Log | Files | Refs | README

ae_common.py (2453B)


      1 # Implementation of the Lilliput-AE tweakable block cipher.
      2 #
      3 # Authors, hereby denoted as "the implementer":
      4 #     Kévin Le Gouguec,
      5 #     Léo Reynaud
      6 #     2019.
      7 #
      8 # For more information, feedback or questions, refer to our website:
      9 # https://paclido.fr/lilliput-ae
     10 #
     11 # To the extent possible under law, the implementer has waived all copyright
     12 # and related or neighboring rights to the source code in this file.
     13 # http://creativecommons.org/publicdomain/zero/1.0/
     14 
     15 """Helper functions used in both Lilliput-I and Lilliput-II."""
     16 
     17 
     18 from .constants import BLOCK_BYTES
     19 from .helpers import xor
     20 from . import tbc
     21 
     22 
     23 def bytes_to_block_matrix(array):
     24     vector = list(array)
     25 
     26     blocks_nb = len(vector)//BLOCK_BYTES
     27 
     28     block_starts = (
     29         i*BLOCK_BYTES for i in range(blocks_nb)
     30     )
     31 
     32     matrix = [
     33         vector[start:start+BLOCK_BYTES] for start in block_starts
     34     ]
     35 
     36     padding_len = len(vector)%BLOCK_BYTES
     37 
     38     if padding_len > 0:
     39         padding = vector[-padding_len:]
     40         matrix.append(padding)
     41 
     42     return matrix
     43 
     44 
     45 def block_matrix_to_bytes(matrix):
     46     return bytes(byte for block in matrix for byte in block)
     47 
     48 
     49 def pad10(X):
     50     zeroes = [0] * (BLOCK_BYTES-len(X)-1)
     51     return X + [0b10000000] + zeroes
     52 
     53 
     54 def integer_to_byte_array(i, n):
     55     return list(i.to_bytes(n, 'big'))
     56 
     57 
     58 def _tweak_associated_data(t, i, padded):
     59     tweak = integer_to_byte_array(i, t//8)
     60 
     61     prefix = 0b0110 if padded else 0b0010
     62 
     63     # Clear upper 4 bits and set them to prefix.
     64     tweak[0] &= 0b00001111
     65     tweak[0] |= prefix << 4
     66 
     67     return tweak
     68 
     69 
     70 def build_auth(t, A, key):
     71     Auth = [0]*BLOCK_BYTES
     72 
     73     l_a = len(A)//BLOCK_BYTES
     74     need_padding = len(A)%BLOCK_BYTES > 0
     75 
     76     A = bytes_to_block_matrix(A)
     77 
     78     for i in range(l_a):
     79         tweak = _tweak_associated_data(t, i, padded=False)
     80         enc = tbc.encrypt(tweak, key, A[i])
     81         Auth = xor(Auth, enc)
     82 
     83     if not need_padding:
     84         return Auth
     85 
     86     tweak = _tweak_associated_data(t, l_a, padded=True)
     87     ad_padded = pad10(A[l_a])
     88     enc = tbc.encrypt(tweak, key, ad_padded)
     89     Auth = xor(Auth, enc)
     90 
     91     return Auth
     92 
     93 
     94 class TagValidationError(Exception):
     95     def __init__(self, announced, computed):
     96         msg = '\n'.join((
     97             'Invalid tag:',
     98             announced.hex().upper()+' (announced)',
     99             computed.hex().upper()+' (computed)'
    100         ))
    101 
    102         super().__init__(msg)
    103         self._announced = announced
    104         self._computed = computed