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

tbc.py (3541B)


      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 """Lilliput-TBC tweakable block cipher.
     16 
     17 This module provides functions to encrypt and decrypt blocks of 128 bits.
     18 """
     19 
     20 from .constants import BLOCK_BYTES, SBOX
     21 from .helpers import xor
     22 from .multiplications import ALPHAS
     23 
     24 
     25 _PERMUTATION = [14, 11, 12, 10, 8, 9, 13, 15, 3, 1, 4, 5, 6, 0, 2, 7]
     26 _PERMUTATION_INV = [13, 9, 14, 8, 10, 11, 12, 15, 4, 5, 3, 1, 2, 6 ,0 ,7]
     27 
     28 
     29 _ROUNDS = {
     30     128: 32,
     31     192: 36,
     32     256: 42
     33 }
     34 
     35 
     36 def _build_tweakey(tweak, key):
     37     return tweak+key
     38 
     39 
     40 def _lane(TK, j):
     41     return TK[j*8:(j+1)*8]
     42 
     43 
     44 def _round_tweakey_schedule(tweakey):
     45     p = len(tweakey)//8
     46 
     47     multiplied_lanes = (
     48         ALPHAS[j](_lane(tweakey, j)) for j in range(p)
     49     )
     50 
     51     return [byte for lane in multiplied_lanes for byte in lane]
     52 
     53 
     54 def _subtweakey_extract(tweakey, Ci):
     55     RTKi = [0]*8
     56 
     57     for j, byte in enumerate(tweakey):
     58         RTKi[j%8] ^= byte
     59 
     60     RTKi[0] ^= Ci
     61 
     62     return RTKi
     63 
     64 
     65 def _tweakey_schedule_whole(tweakey, r):
     66     TKs = [tweakey]
     67     RTKs = [_subtweakey_extract(TKs[0], 0)]
     68 
     69     for i in range(1, r):
     70         TKs.append(_round_tweakey_schedule(TKs[i-1]))
     71         RTKs.append(_subtweakey_extract(TKs[i], i))
     72 
     73     return RTKs
     74 
     75 
     76 def _non_linear_layer(state, subtweakey):
     77     variables_xored = xor(state, subtweakey)
     78 
     79     variables_sboxed = [
     80         SBOX[variables_xored[i]] for i in range(8)
     81     ]
     82 
     83     state_output = state
     84     for i in range(8):
     85         state_output[15-i] ^= variables_sboxed[i]
     86 
     87     return state_output
     88 
     89 
     90 def _linear_layer(state):
     91     state_output = state
     92 
     93     for byte in range(1, 8):
     94         state_output[15] ^= state[byte]
     95 
     96     for byte in range(9, 15):
     97         state_output[byte] ^= state[7]
     98 
     99     return state_output
    100 
    101 
    102 def _permutation_layer(state, p):
    103     return [
    104         state[p[i]] for i in range(BLOCK_BYTES)
    105     ]
    106 
    107 
    108 def _one_round_egfn_enc(state, subtweakey):
    109     state_non_linear = _non_linear_layer(state, subtweakey)
    110     state_linear = _linear_layer(state_non_linear)
    111     state_permutation = _permutation_layer(state_linear, _PERMUTATION)
    112 
    113     return state_permutation
    114 
    115 
    116 def _last_round_egfn(state, subtweakey):
    117     state_non_linear = _non_linear_layer(state, subtweakey)
    118     state_linear = _linear_layer(state_non_linear)
    119 
    120     return state_linear
    121 
    122 
    123 def _one_round_egfn_dec(state, subtweakey):
    124     state_non_linear = _non_linear_layer(state, subtweakey)
    125     state_linear = _linear_layer(state_non_linear)
    126     state_permutation = _permutation_layer(state_linear, _PERMUTATION_INV)
    127 
    128     return state_permutation
    129 
    130 
    131 def encrypt(tweak, key, message):
    132     r = _ROUNDS[8*len(key)]
    133 
    134     tweakey = _build_tweakey(tweak, key)
    135     RTKs = _tweakey_schedule_whole(tweakey, r)
    136 
    137     state = message
    138 
    139     for i in range(r-1):
    140         state = _one_round_egfn_enc(state, RTKs[i])
    141 
    142     return _last_round_egfn(state, RTKs[r-1])
    143 
    144 
    145 def decrypt(tweak, key, cipher):
    146     r = _ROUNDS[8*len(key)]
    147 
    148     tweakey = _build_tweakey(tweak, key)
    149     RTKs = _tweakey_schedule_whole(tweakey, r)
    150 
    151     state = cipher
    152 
    153     for i in range(r-1):
    154         state = _one_round_egfn_dec(state, RTKs[r-i-1])
    155 
    156     return _last_round_egfn(state, RTKs[0])