summaryrefslogtreecommitdiff
path: root/src/add_python/lilliput/tbc.py
blob: 50f9e2f87f468eb78b71da6089f70129f6cc0bc2 (plain)
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# 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-TBC tweakable block cipher.

This module provides functions to encrypt and decrypt blocks of 128 bits.
"""

from .constants import BLOCK_BYTES, Sbox
from .multiplications import ALPHAS


_permutation = [14, 11, 12, 10, 8, 9, 13, 15, 3, 1, 4, 5, 6, 0, 2, 7]
_permutationInv = [13, 9, 14, 8, 10, 11, 12, 15, 4, 5, 3, 1, 2, 6 ,0 ,7]

################################################################################

def _BuildTweakey(tweak, key):
    return tweak+key

#############################

def _Lane(TK, j):
    return TK[j*8:(j+1)*8]


def _RoundTweakeySchedule(tweakey):
    p = len(tweakey)//8

    multiplied_lanes = (
        ALPHAS[j](_Lane(tweakey, j)) for j in range(p)
    )

    return [byte for lane in multiplied_lanes for byte in lane]


def _SubTweakeyExtract(tweakey, Ci):
    RTKi = [0]*8

    for j, byte in enumerate(tweakey):
        RTKi[j%8] ^= byte

    RTKi[0] ^= Ci

    return RTKi


def _TweakeyScheduleWhole(tweakey, r):
    # Store the initial tweakey in TKs[0], and the corresponding round tweakey
    # in RTKs[0].
    TKs = [tweakey]
    RTKs = [_SubTweakeyExtract(TKs[0], 0)]

    for i in range(1, r):
        TKs.append(_RoundTweakeySchedule(TKs[i-1]))
        RTKs.append(_SubTweakeyExtract(TKs[i], i))

    return RTKs


################################################################################

def _NonLinearLayer(state, subtweakey):

    variables_xored = [0 for byte in range(0, 8)]
    for byte in range(0,8):
        variables_xored[byte] = state[byte] ^ subtweakey[byte]

    variables_sboxed = [0 for byte in range(0, 8)]
    for byte in range(0, 8):
        variables_sboxed[byte] = Sbox[variables_xored[byte]]

    state_output = [0 for byte in range(0, BLOCK_BYTES)]
    for byte in range(0,BLOCK_BYTES):
        state_output[byte] = state[byte]
    for byte in range(0, 8):
        state_output[15 - byte] ^= variables_sboxed[byte]

    return state_output


def _LinearLayer(state):
    state_output = [0 for byte in range(0, BLOCK_BYTES)]
    for byte in range(0, BLOCK_BYTES):
        state_output[byte] = state[byte]

    for byte in range(1, 8):
        state_output[15] ^= state[byte]

    for byte in range(9, 15):
        state_output[byte] ^= state[7]

    return state_output


def _PermutationLayerEnc(state):
    state_output = [0 for byte in range(0, BLOCK_BYTES)]
    for byte in range(0, BLOCK_BYTES):
        state_output[byte] = state[_permutation[byte]]

    return state_output

def _PermutationLayerDec(state):
    state_output = [0 for byte in range(0, BLOCK_BYTES)]
    for byte in range(0, BLOCK_BYTES):
        state_output[byte] = state[_permutationInv[byte]]

    return state_output


def _OneRoundEGFNEnc(state, subtweakey):
    state_non_linear = _NonLinearLayer(state, subtweakey)
    state_linear = _LinearLayer(state_non_linear)
    state_permutation = _PermutationLayerEnc(state_linear)

    return state_permutation

def _LastRoundEGFN(state, subtweakey):
    state_non_linear = _NonLinearLayer(state, subtweakey)
    state_linear = _LinearLayer(state_non_linear)

    return state_linear


def _OneRoundEGFNDec(state, subtweakey):
    state_non_linear = _NonLinearLayer(state, subtweakey)
    state_linear = _LinearLayer(state_non_linear)
    state_permutation = _PermutationLayerDec(state_linear)

    return state_permutation


def _Rounds(key_bytes):
    rounds = {
        128: 32,
        192: 36,
        256: 42
    }
    return rounds[key_bytes*8]


################################################################################


def encrypt(tweak, key, message):
    r = _Rounds(len(key))

    tweakey = _BuildTweakey(tweak, key)
    RTKs = _TweakeyScheduleWhole(tweakey, r)

    state = [0 for byte in range(0, BLOCK_BYTES)]
    for byte in range(0, BLOCK_BYTES):
        state[byte] = message[byte]

    for i in range(0, r-1):
        state_output = _OneRoundEGFNEnc(state, RTKs[i])

        for byte in range(0, BLOCK_BYTES):
            state[byte] = state_output[byte]

    state_output = _LastRoundEGFN(state, RTKs[r-1])

    return state_output


def decrypt(tweak, key, cipher):
    r = _Rounds(len(key))

    tweakey = _BuildTweakey(tweak, key)
    RTKs = _TweakeyScheduleWhole(tweakey, r)

    state = [0 for byte in range(0, BLOCK_BYTES)]
    for byte in range(0, BLOCK_BYTES):
        state[byte] = cipher[byte]

    for i in range(0, r-1):
        state_output = _OneRoundEGFNDec(state, RTKs[r-i-1])

        for byte in range(0, BLOCK_BYTES):
            state[byte] = state_output[byte]

    state_output = _LastRoundEGFN(state, RTKs[0])

    return state_output